package com.framework.service.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.framework.dto.FileUrlDTO;
import com.framework.dto.MergeFileDTO;
import com.framework.dto.PageRequestDTO;
import com.framework.entity.ChunkFile;
import com.framework.entity.MergeFile;
import com.framework.entity.User;
import com.framework.minio.MinioClientConfig;
import com.framework.minio.MinioUtil;
import com.framework.repository.ChunkFileRepository;
import com.framework.repository.MergeFileRepository;
import com.framework.repository.UserRepository;
import com.framework.service.MergeFileService;
import com.framework.utiles.FileUrlUtils;
import com.framework.vo.MergeFileVO;
import io.minio.MinioClient;
import io.minio.RemoveObjectArgs;
import io.minio.errors.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @Description: 合并文件服务接口实现
 * @Author: JamesYao
 * @DateTime: 2024/1/17 0:29
 */
@Service
@RequiredArgsConstructor
@Transactional
public class MergeFileServiceImpl implements MergeFileService {

    private final UserRepository userRepository;

    private final MergeFileRepository mergeFileRepository;

    private final ChunkFileRepository chunkFileRepository;

    private final MinioUtil minioUtil;

    private final MinioClientConfig minioClientConfig;

    /**
     * 保存合并文件
     *
     * @param file 合并文件对象
     */
    @Override
    public void saveMergeFile(MergeFile file) {
        mergeFileRepository.save(file);
    }

    /**
     * 获取文件的总文件ID
     *
     * @param file 合并文件对象
     * @return SaResult 合并文件ID的结果对象
     */
    @Override
    public SaResult getSumFileId(MergeFile file) {
        if (Objects.isNull(file.getFileId())) {
            return SaResult.error("上传文件错误");
        }
        SaSession session = StpUtil.getSession();
        // 获取当前用户ID
        UUID userId = (UUID) session.get("userId");
        if (Objects.isNull(userId)) {
            return SaResult.error("用户不存在");
        }
        User byId = userRepository.findById(userId).orElseThrow(() -> new RuntimeException("用户不存在"));
        String name = byId.getName();
        MergeFile merge = new MergeFile();
        BeanUtils.copyProperties(file, merge);
        merge.setUserName(name);
        merge.setUserId(userId);
        merge.setType(0);
        this.saveMergeFile(merge);
        MergeFile mergeFile = mergeFileRepository.findByFileIdAndUserId(file.getFileId(),userId);
        if (Objects.isNull(mergeFile)) {
            return SaResult.error("文件不存在");
        }
        return SaResult.data(mergeFile.getId());
    }


    /**
     * 检查文件的完整性
     *
     * @param file 要检查的文件对象
     * @return 检查结果对象
     */
    @Override
    public SaResult checkFileIntegrity(MergeFile file) {
        UUID userId = (UUID) StpUtil.getSession().get("userId");
        String hash = file.getHash();
        List<MergeFile> mergeFiles = mergeFileRepository.findByHash(hash);
        if (mergeFiles.isEmpty()) {
            return SaResult.ok().setCode(211).setData("文件未上传");
        }
        for (MergeFile mergeFile : mergeFiles) {
            UUID id = mergeFile.getId();
            List<ChunkFile> byPid = chunkFileRepository.findByPid(id);
            for (ChunkFile chunkFile : byPid) {
                // 如果所有ChunkFile对象的计数等于列表的长度，则表示文件已完整上传
                if (chunkFile.getCount() == byPid.size()) {
                    if (mergeFile.getUserId().equals(userId)) {
                        return SaResult.ok().setCode(212).setData("文件已完整上传");
                    }
                    String name = userRepository.findById(userId).orElseThrow(() -> new RuntimeException("用户不存在")).getName();
                    file.setUserId(userId);
                    file.setUserName(name);
                    mergeFileRepository.save(file);
                    return SaResult.ok().setCode(214).setData("文件已秒传");
                }
            }
        }
        List<MergeFile> hash1 = mergeFileRepository.findByHash(hash);
        List<ChunkFile> result = new ArrayList<>();
        for (MergeFile mergeFile : hash1) {
            List<ChunkFile> chunkFileList = chunkFileRepository.findByFileIdOrderByNumAsc(mergeFile.getFileId());
            if (!chunkFileList.isEmpty()){
                result = chunkFileList;
            }
        }
        return SaResult.ok().setCode(213).setData(result);
    }

    /**
     * 根据用户ID获取合并文件列表
     *
     * @return 合并文件列表对象
     */
    @Override
    public SaResult getMergeByUserIdAndPid(String pid) {
        // 获取当前用户ID
        UUID userId = (UUID) StpUtil.getSession().get("userId");
        if (Objects.isNull(userId)) {
            return SaResult.error("当前登录用户不存在");
        }
        List<MergeFile> byUserId = mergeFileRepository.findByUserIdAndPidAndDeletedAtIsNull(userId,pid);
        return SaResult.data(byUserId);
    }

    /**
     * 根据用户ID获取已删除合并文件列表
     *
     * @return 已删除合并文件列表对象
     */
    @Override
    public SaResult getDeleteMergeByUserIdAndPid(PageRequestDTO pageRequestDTO) {
        SaSession session = StpUtil.getSession();
        // 获取当前用户ID
        UUID userId = (UUID) session.get("userId");
        if (Objects.isNull(userId)) {
            return SaResult.error("当前登录用户不存在");
        }
//        List<MergeFile> byUserId = mergeFileRepository.findByUserIdAndDeletedAtIsNotNull(userId);
        Pageable pageable = PageRequest.of(pageRequestDTO.getPage(), pageRequestDTO.getSize());
        Page<MergeFile> byUserId = mergeFileRepository.findByUserIdAndDeletedAtIsNotNullOrderByCreatedAtDesc(userId, pageable);
        List<MergeFileVO> resultList =  new ArrayList<>();
        for (MergeFile mergeFile : byUserId){
            MergeFileVO mergeFileVO = new MergeFileVO();
            LocalDateTime deletedAt = mergeFile.getDeletedAt();
            LocalDateTime now = LocalDateTime.now();
            if (now.isBefore(deletedAt.plusMonths(1))) {
                BeanUtils.copyProperties(mergeFile,mergeFileVO);
                Duration between = Duration.between(now,deletedAt.plusMonths(1));
                mergeFileVO.setTimeLeft(between.toDays());
                mergeFileVO.setSize(mergeFile.getSize()/(1024*1024));
                resultList.add(mergeFileVO);
            }
        }
        Page<MergeFileVO> pageResult = new PageImpl<>(resultList, pageable, byUserId.getTotalElements());
        return SaResult.data(pageResult);
    }

    /**
     * 新建文件夹
     *
     * @param file 合并文件
     * @return SaResult 返回结果
     */
    @Override
    public SaResult newFolder(MergeFile file) {
        // 获取当前用户ID
        SaSession session = StpUtil.getSession();
        UUID userId = (UUID) session.get("userId");
        if (Objects.isNull(userId)) {
            return SaResult.error("当前登录用户不存在");
        }
        // 检查当前目录下是否已存在重名文件夹
        List<MergeFile> byUserId = mergeFileRepository.findByUserId(userId);
        if (Objects.nonNull(byUserId)) {
            boolean duplicateFolderExists = byUserId.stream().anyMatch(mergeFile ->
                    mergeFile.getName().equals(file.getName())
                            && mergeFile.getType() == 1
                            && mergeFile.getPid().equals(file.getPid()));
            if (duplicateFolderExists) {
                return SaResult.error("当前目录下已存在重名文件夹");
            }
        }

        User userById = userRepository.findById(userId).orElseThrow(() -> new RuntimeException("用户不存在"));
        String name = userById.getName();
        file.setUserName(name);
        file.setUserId(userId);
        // 设置文件夹类型为1，并保存合并文件
        file.setType(1);
        file.setPid("0");
        this.saveMergeFile(file);
        return SaResult.ok("新增目录成功");
    }

    /**
     * 根据文件ID删除文件
     *
     * @param fileId 文件ID
     * @return 删除结果
     */
    @Override
    public SaResult deleteFileByFileId(String fileId) {
        UUID userId = (UUID) StpUtil.getSession().get("userId");
        MergeFile mergeFile = mergeFileRepository.findByFileIdAndUserId(fileId,userId);
        if (Objects.isNull(mergeFile)) {
            SaResult.error("文件不存在");
        }
        //  文件类型为文件
        if (mergeFile.getType() == 0){
            this.deleteFilesByFileId(fileId);
        }
        //  文件类型为文件夹
        if (mergeFile.getType() == 1){
            UUID id = mergeFile.getId();
            List<MergeFile> byPid = mergeFileRepository.findByPid(id.toString());
            if (Objects.nonNull(byPid)){
                for (MergeFile merge : byPid) {
                    String fileId1 = merge.getFileId();
                    this.deleteFilesByFileId(fileId1);
                }
            }
            mergeFileRepository.deleteById(id);
        }
        return SaResult.ok("删除成功");
    }

    /**
     * 重命名合并文件
     * @param file 合并文件DTO对象
     */
    @Override
    public void renameMergeFile(MergeFileDTO file) {
        UUID userId =(UUID) StpUtil.getSession().get("userId");
        MergeFile mergeFile = mergeFileRepository.findByFileIdAndUserId(file.getFileId(),userId);
        if (Objects.isNull(mergeFile)){
            return;
        }
        mergeFile.setName(file.getFileName());
        mergeFileRepository.save(mergeFile);
    }

    /**
     * 根据文件ID恢复文件
     * @param fileId 文件ID
     */
    @Override
    public void recoverFileByFileId(String fileId) {
        UUID userId = (UUID) StpUtil.getSession().get("userId");
        MergeFile mergeFile = mergeFileRepository.findByFileIdAndUserId(fileId, userId);
        mergeFile.setDeletedAt(null);
        mergeFile.setUpdatedAt(LocalDateTime.now());
    }

    /**
     * 通过文件ID假删除文件
     * @param fileId 文件ID
     */
    @Override
    public void fakeDeleteFileByFileId(String fileId) {
        UUID userId = (UUID) StpUtil.getSession().get("userId");
        MergeFile mergeFile = mergeFileRepository.findByFileIdAndUserId(fileId,userId);
        if (Objects.isNull(mergeFile)){
            return;
        }
        int type = mergeFile.getType();
        //  类型为文件
        if (type == 0){
            this.fakeDeleteFilesByFileId(fileId);
        }
        //  类型为文件夹
        if (type == 1){
            UUID id = mergeFile.getId();
            List<MergeFile> byPid = mergeFileRepository.findByPid(id.toString());
            if (Objects.nonNull(byPid)){
                for (MergeFile merge : byPid) {
                    String fileId1 = merge.getFileId();
                    this.fakeDeleteFilesByFileId(fileId1);
                }
            }
            //  不保留文件夹
            mergeFileRepository.deleteById(id);
        }
    }

    /**
     * 根据文件ID删除文件（假删，添加deletedAt）
     *
     */
    public void fakeDeleteFilesByFileId(String fileId){
        UUID userId = (UUID) StpUtil.getSession().get("userId");
        MergeFile mergeFile = mergeFileRepository.findByFileIdAndUserId(fileId,userId);
        mergeFile.setDeletedAt(LocalDateTime.now());
        mergeFileRepository.save(mergeFile);
    }

    /**
     * 根据文件ID删除文件（文件直接删除）
     *
     */
    public void deleteFilesByFileId(String fileId){
        List<ChunkFile> chunks = chunkFileRepository.findByFileId(fileId);
        for (ChunkFile chunkFile : chunks) {
            FileUrlDTO fileUrl = FileUrlUtils.getFileUrl(chunkFile.getPath());
            if (Objects.isNull(fileUrl)){
                return ;
            }
            // 创建MinIO客户端
            MinioClient minioClient = minioClientConfig.minioClient();
            try {
                minioClient.removeObject(RemoveObjectArgs.builder()
                        .bucket(fileUrl.getBucketName())
                        .object(fileUrl.getName())
                        .build());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            chunkFileRepository.deleteChunksByFileId(fileId);
        }
        mergeFileRepository.deleteByFileId(fileId);
    }

    @Override
    public void downloadFileByFileId(String fileId, HttpServletResponse response)
            throws IOException, ServerException, InsufficientDataException,
            ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException,
            InvalidResponseException, XmlParserException, InternalException {
        UUID userId = (UUID) StpUtil.getSession().get("userId");
        MergeFile mergeFile = mergeFileRepository.findByFileIdAndUserId(fileId,userId);
        if (Objects.isNull(mergeFile)) {
            return;
        }
        List<MergeFile> byHash = mergeFileRepository.findByHash(mergeFile.getHash());
        for (MergeFile mergeFile1 : byHash) {
            List<ChunkFile> byFileId = chunkFileRepository.findByFileIdOrderByNumAsc(mergeFile1.getFileId());
            if (!byFileId.isEmpty()){
                ServletOutputStream outputStream = response.getOutputStream();
                // 设置自定义响应头hash
                response.setContentType("application/octet-stream");
                response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,hash");
                response.setHeader("hash", mergeFile.getHash());
                for (ChunkFile chunkFile : byFileId) {
                    String path = chunkFile.getPath();
                    FileUrlDTO fileUrl = FileUrlUtils.getFileUrl(path);
                    assert fileUrl != null;
                    InputStream inputStream = minioUtil.downloadOutputStream(fileUrl.getName(), fileUrl.getBucketName());
                    int len = 0;
                    byte[] b = new byte[2048];
                    while ((len = inputStream.read(b)) != -1) {
                        outputStream.write(b, 0, len);
                    }
                    inputStream.close();
                }
                outputStream.close();
            }
        }

    }


}
